import json
import requests

from django.shortcuts import render, reverse
from django.http import JsonResponse, HttpResponse
from django.forms import model_to_dict
from django.views.decorators.csrf import csrf_exempt

from web.forms.file import FileFolderModelForm, FileModelForm
from web import models
from projectmanager import settings
from utils.tencent.cos import CosTencent


def file(request, project_id):
    """ 文件列表 & 添加文件夹 """

    """
    1 - 如果插入的文件夹在根目录下，url：http://127.0.0.1:8000/mamage/1/file/
    2 - 如果插入的文件夹不在根目录下，url: http://127.0.0.1:8000/manage/1/file/?folder_id=1
    """
    parent_object = None
    folder_id = request.GET.get('folder_id')
    if folder_id:  # 如果url传入folder_id这个参数，说明已经进入了子目录中
        try:
            # folder_id 必须是一个数字，不能是字符串或者是其他格式
            folder_id = int(folder_id)
            # 拿着id去数据库中获取文件夹对象
            parent_object = models.FileRepository.objects.filter(id=folder_id, project_id=project_id).first()
        except Exception:  # ValueError or TypeError
            pass
    # GET 请求展示所有文件
    if request.method == 'GET':

        bread_crumb_list = []
        file_object = parent_object
        while file_object:
            bread_crumb_list.insert(0, model_to_dict(file_object, ['id', 'name']))
            file_object = file_object.parent

        query_set = models.FileRepository.objects.filter(project_id=project_id).order_by(
            '-file_type')  # 按照类型倒序排列，因为文件夹类型为2，所以要倒排列
        if parent_object:
            file_list = query_set.filter(parent=parent_object).all()
        else:
            file_list = query_set.filter(parent__isnull=True).all()
        add_file_form = FileFolderModelForm(request, parent_object)
        context = {
            'add_file_form': add_file_form,
            'file_list': file_list,
            'bread_crumb_list': bread_crumb_list,
            'folder_object': parent_object
        }
        return render(request, 'web/file.html', context)
    # POST请求 添加文件夹 & 编辑文件夹
    edit_folder_id = request.POST.get('edit-folder-id')
    try:
        edit_folder_id = int(edit_folder_id)
        edit_folder_object = models.FileRepository.objects.filter(id=edit_folder_id, project_id=project_id).first()
    except Exception:
        edit_folder_object = None
    if edit_folder_object:
        # 编辑
        form = FileFolderModelForm(request, parent_object, data=request.POST, instance=edit_folder_object)
    else:
        # 添加
        form = FileFolderModelForm(request, parent_object, data=request.POST)
    if form.is_valid():
        # 文件所属项目
        form.instance.project = request.login_user.project
        # 类型是文件夹
        form.instance.file_type = 2
        # 更新者是当前登录的用户
        form.instance.update_user = request.login_user.user
        # 插入父目录，如果没有那么就在根目录，有没有父目录看的是在哪个url中点击的新建文件夹，有?folder_id 就不是根目录，没有就是
        form.instance.parent = parent_object
        form.save()
        # 添加文件夹到数据库
        return JsonResponse({'status': True})
    else:
        # form.errors : {name : [''',]}
        return JsonResponse({'status': False, 'error': form.errors})


def file_delete(request, project_id):
    """ 删除文件&文件夹操作 """
    # 实例化一个COS对象，用这个对象操作COS存储桶中的文件
    cos = CosTencent()
    # 过滤file_id必须是一个int类型，如果报错直接返回False，不报错才能去数据库查询
    file_id = request.GET.get('deleteFolderId')
    try:
        file_id = int(file_id)
    except Exception:
        return JsonResponse({'status': False})
    else:
        # 数据库中获取文件对象
        delete_object = models.FileRepository.objects.filter(id=file_id, project_id=project_id).first()
    # 情况1-如果删除的是文件夹
    if delete_object.file_type == 2:
        # 存储所有要删除的key的列表
        total_file_key_list = []
        # 存储所有要删除的文件的字节大小
        total_file_size = 0
        # 获取父目录是要删除文件对象的所有对象
        file_list = list(models.FileRepository.objects.filter(parent=delete_object).all())
        while len(file_list):
            current_file_object = file_list.pop()
            if current_file_object.file_type == 1:
                # 如果是文件
                total_file_key_list.append({
                    'Key': current_file_object.key
                })
                total_file_size += current_file_object.file_size
            else:
                file_list.extend(list(models.FileRepository.objects.filter(parent=current_file_object).all()))
        # COS中删除文件
        if total_file_key_list:
            cos.delete_file_list(bucket_name=request.login_user.project.bucket_name,
                                 key_list=total_file_key_list)
        # 释放项目存储空间
        request.login_user.project.use_space -= total_file_size
        request.login_user.project.save()
        # 数据库中删除对象 级联删除，会自动删除其下子目录
        delete_object.delete()
    # 如果删除的是文件
    if delete_object.file_type == 1:
        # 1 删除COS中的文件
        cos.delete_file(
            bucket_name=request.login_user.project.bucket_name,
            key=delete_object.key
        )
        # 2 归还使用空间
        request.login_user.project.use_space -= delete_object.file_size
        # 2.1 提交数据库保存
        request.login_user.project.save()
        # 删除对象
        delete_object.delete()
    return JsonResponse({'status': True})


@csrf_exempt
def cos_credential(request, project_id):
    """ 给前端传递COS临时密钥，用于前端直接上传文件 """
    from sts.sts import Sts
    # print(request.body) # b'[{"name":"left-top3.jpg","size":23245},{"name":"resource-use-data.jpg","size":4991}]'
    # print(request.body.decode(encoding='utf8')) # [{"name":"left-top3.jpg","size":23245},{"name":"resource-use-data.jpg","size":4991}]
    upload_file_list = json.loads(request.body.decode(encoding='utf8'))
    per_file_limit = request.login_user.price_policy.per_file_size * 1024 * 1024  # 单位是MB，转换成字节B
    all_file_limit = request.login_user.price_policy.project_space * 1024 * 1024 * 1024  # 单位是GB，转换成字节B
    all_file_size = 0
    for file in upload_file_list:
        # 判断单文件大小是否超限
        if file['size'] > per_file_limit:
            return JsonResponse({'status': False, 'error': f'{file["name"]}单文件超限'})
        all_file_size += file['size']
        # 判断总文件大小是否超限
        request.login_user.project.use_space += all_file_size
        if request.login_user.project.use_space > all_file_limit:
            return JsonResponse({'status': False, 'error': '总文件超限'})

    config = {
        # 临时密钥有效时长，单位是秒（30分钟=1800秒）
        'duration_seconds': 1800,
        # 固定密钥 id
        'secret_id': settings.TENCENT_COS_SECRET_ID,
        # 固定密钥 key
        'secret_key': settings.TENCENT_COS_SECRET_KEY,
        # 换成你的 bucket
        'bucket': request.login_user.project.bucket_name,
        # 换成 bucket 所在地区
        'region': request.login_user.project.bucket_region,
        # 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径
        # 例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
        'allow_prefix': '*',
        # 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
        'allow_actions': [
            'name/cos:PostObject',
            # 'name/cos:DeleteObject',
            # "name/cos:UploadPart",
            # "name/cos:UploadPartCopy",
            # "name/cos:CompleteMultipartUpload",
            # "name/cos:AbortMultipartUpload",
            "*",
        ],
    }
    sts = Sts(config)
    result_dict = sts.get_credential()
    return JsonResponse({'status': True, 'data': result_dict})


@csrf_exempt
def file_post(request, project_id):
    """ 文件上传成功后给这个链接发请求，将上传成功的文件写到数据库中 """
    print(request.POST)
    form = FileModelForm(request, data=request.POST)
    if form.is_valid():
        data_dict = form.cleaned_data
        data_dict.pop('etag')
        data_dict.update(
            {'project': request.login_user.project, 'file_type': 1, 'update_user': request.login_user.user})
        instance = models.FileRepository.objects.create(**data_dict)
        # 项目的已使用空间：更新 (data_dict['file_size'])
        request.login_user.project.use_space += data_dict['file_size']
        request.login_user.project.save()

        result = {
            'id': instance.id,
            'name': instance.name,
            'file_size': instance.file_size,
            'username': instance.update_user.username,
            'datetime': instance.update_datetime.strftime("%Y年%m月%d日 %H:%M"),
            'download_url': reverse('file_download', kwargs={"project_id": project_id, 'file_id': instance.id}),
            'file_path': instance.file_path,
            # 'file_type': instance.get_file_type_display()
        }
        return JsonResponse({'status': True, 'data': result})
    return JsonResponse({'status': False, 'data': '文件错误'})


def file_download(request, project_id, file_id):
    """ 文件下载 """
    file_object = models.FileRepository.objects.filter(id=file_id, project_id=project_id).first()
    res = requests.get(file_object.file_path)

    # 文件分块处理（适用于大文件）
    data = res.iter_content()

    # 设置content_type=application/octet-stream 用于提示下载框
    response = HttpResponse(data, content_type="application/octet-stream")
    from django.utils.encoding import escape_uri_path

    # 设置响应头：中文件文件名转义
    response['Content-Disposition'] = "attachment; filename={};".format(escape_uri_path(file_object.name))
    return response
